﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Borg.Framework
{
    public class ListCommand : ICommand
    {
        private readonly string[] _arguments =
            new[]
                {
                    "PluginInfos",
                    "ExtensionTypeInfos",
                    "PlugTypeInfos",
                    "ExtensionInfos",
                    "SlotInfos",
                    "PlugInfos"
                };

        private readonly Dictionary<string, CommandHandler> _listCommandHandler = new Dictionary<string, CommandHandler>();
        
        internal static readonly string listFormat = "{0,4}  {1,-10}  {2}{3}";

        public ListCommand()
        {
            _listCommandHandler.Add("PluginInfos", ListPluginInfos);
            _listCommandHandler.Add("ExtensionTypeInfos", ListExtensionTypeInfos);
            _listCommandHandler.Add("ExtensionInfos", ListExtensionInfos);
            _listCommandHandler.Add("PlugTypeInfos", ListPlugTypeInfos);
            _listCommandHandler.Add("SlotInfos", ListSlotInfos);
            _listCommandHandler.Add("PlugInfos", ListPlugInfos);
        }

        public IEnumerable<string> GetArguments(string[] command, int pos)
        {
            return _arguments;
        }

        internal static string GetSlotStateAsText(SlotState s)
        {
            switch (s)
            {
                case SlotState.Discovered:
                    return "DISCOVERED";

                case SlotState.Qualified:
                    return "QUALIFIED";

                case SlotState.Open:
                    return "OPEN";
            }

            Debug.Assert(false);
            return "UNKNOWN";
        }

        internal static string GetStateAsText(State s)
        {
            switch (s)
            {
                case State.Discovered:
                    return "DISCOVERED";

                case State.Qualified:
                    return "QUALIFIED";

                case State.Registered:
                    return "REGISTERED";

                case State.Plugged:
                    return "PLUGGED";

                case State.Selected:
                    return "SELECTED";
            }

            Debug.Assert(false);
            return "UNKNOWN";
        }

        private string List(string[] command)
        {
            if (command.Length > 1)
            {
                CommandHandler handler;

                return _listCommandHandler.TryGetValue(command[1], out handler)
                    ? handler(command)
                    : string.Format("'{0}' not found.", command[1]);
            }

            return string.Empty;
        }

        private static string ListExtensionInfos(string[] command)
        {
            var builder = new StringBuilder();

            if (command.Length == 2)
            {
                builder.AppendFormat(listFormat, new object[] { "Id", "State", "ExtensionInfo", RuntimeCommands.lf });
                
                using (var enumerator = Runtime.Repository.ExtensionInfos.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        var ei = enumerator.Current;
                        builder.AppendFormat(
                            listFormat,
                            new object[]
                                {
                                    ei.Id,
                                    GetStateAsText(StateHelper.GetState(ei)),
                                    ei.Name,
                                    RuntimeCommands.lf
                                });
                    }
                }

                builder.AppendFormat("  {0} item(s).{1}", Runtime.Repository.ExtensionInfos.Count, RuntimeCommands.lf);
            }
            else
            {
                var index = command[2].IndexOf('"');
                var lastIndex = command[2].LastIndexOf('"');
                var extensionInfos = new ExtensionInfo[0];

                if (index == -1)
                {
                    int item;

                    if (int.TryParse(command[2], out item))
                    {
                        var ei = Runtime.Repository.ExtensionInfos.Find(item);

                        if (ei != null)
                            extensionInfos = new[] {ei};
                    }
                }
                else if (lastIndex != -1 && lastIndex > index)
                {
                    var name = command[2].Substring(index + 1, (lastIndex - index) - 1);
                    extensionInfos = Runtime.Repository.ExtensionInfos.Find(name);
                }

                if (command.Length == 3)
                {
                    builder.AppendFormat(listFormat,
                                         new object[] {"Id", "State", "ExtensionInfo", RuntimeCommands.lf});

                    foreach (var ei in extensionInfos)
                        builder.AppendFormat(
                            listFormat,
                            new object[]
                                {
                                    ei.Id,
                                    GetStateAsText(StateHelper.GetState(ei)),
                                    ei.Name,
                                    RuntimeCommands.lf
                                });

                    builder.AppendFormat("  {0} item(s).{1}", extensionInfos.Length, RuntimeCommands.lf);
                }
                else
                    foreach (var ei in extensionInfos)
                        ei.PlugPlugs();
            }

            return builder.ToString();
        }

        private static string ListExtensionTypeInfos(string[] command)
        {
            var builder = new StringBuilder();

            builder.AppendFormat(listFormat,
                new object[] { "Id", "State", "ExtensionTypeInfo", RuntimeCommands.lf });

            foreach (var eti in Runtime.Repository.ExtensionTypeInfos)
                builder.AppendFormat(
                    listFormat,
                    new object[]
                        {
                            eti.Id,
                            GetStateAsText(
                                StateHelper.GetState(eti)),
                            eti.Name,
                            RuntimeCommands.lf
                        });

            builder.AppendFormat("  {0} item(s).{1}",
                Runtime.Repository.ExtensionTypeInfos.Count, RuntimeCommands.lf);

            return builder.ToString();
        }

        private static string ListPlugInfos(string[] command)
        {
            var builder = new StringBuilder();

            builder.AppendFormat(listFormat,
                new object[] { "Id", "State", "Plug", RuntimeCommands.lf });

            var list = new SortedList<int, PlugInfo>();

            if (command.Length == 2)
                foreach (var pi in Runtime.Repository.PlugInfos)
                    list.Add(pi.Id, pi);
            else if (command.Length >= 3)
            {
                var match = new Regex("=([0-9]+)").Match(command[2]);
                var result = 0;

                if (match.Success)
                    int.TryParse(match.Groups[1].Value, out result);

                foreach (var pi in Runtime.Repository.PlugInfos.
                    Where(pi => pi.ExtensionInfo.Id == result))
                    list.Add(pi.Id, pi);
            }

            foreach (var pi in list.Values)
                builder.AppendFormat(
                    listFormat,
                    new object[]
                        {
                            pi.Id,
                            GetStateAsText(
                                StateHelper.GetState(pi)),
                            string.Format(
                                "{0} ({1}:{2})",
                                pi.Name,
                                pi.ExtensionInfo.Id,
                                pi.ExtensionInfo.Name),
                            RuntimeCommands.lf
                        });

            builder.AppendFormat("  {0} item(s).{1}", 0, RuntimeCommands.lf);

            return builder.ToString();
        }

        private static string ListPluginInfos(string[] command)
        {
            var builder = new StringBuilder();

            if (command.Length == 2)
            {
                builder.AppendFormat(listFormat,
                    new object[] { "Id", "State", "PluginInfo", RuntimeCommands.lf });

                foreach (PluginInfo pi in Runtime.Repository.PluginInfos)
                    builder.AppendFormat(
                        listFormat,
                        new object[]
                            {
                                pi.Id,
                                GetStateAsText(
                                    StateHelper.GetState(pi)),
                                string.Format(
                                    "{0}_{1}",
                                    pi.Name,
                                    pi.Version),
                                RuntimeCommands.lf
                            });

                builder.AppendFormat("  {0} item(s).{1}",
                    Runtime.Repository.PluginInfos.Count, RuntimeCommands.lf);

                return builder.ToString();
            }

            return string.Empty;
        }

        private static string ListPlugTypeInfos(string[] command)
        {
            var builder = new StringBuilder();

            if (command.Length == 2)
            {
                builder.AppendFormat(listFormat,
                    new object[] { "Id", "State", "PlugTypeInfo", RuntimeCommands.lf });

                var i = 0;

                foreach (var pti in Runtime.Repository.PlugTypeInfos)
                {
                    builder.AppendFormat(
                        listFormat,
                        new object[]
                            {
                                pti.Id,
                                GetStateAsText(StateHelper.GetState(pti)),
                                pti.Name,
                                RuntimeCommands.lf
                            });
                    i++;
                }

                builder.AppendFormat("  {0} item(s).{1}", i, RuntimeCommands.lf);
            }
            else
            {
                var index = command[2].IndexOf('"');
                var lastIndex = command[2].LastIndexOf('"');

                if (index != -1 && lastIndex != -1 && lastIndex > index)
                {
                    var name = command[2].Substring(index + 1, (lastIndex - index) - 1);
                    var plugType = Runtime.Repository.PlugTypeInfos.Find(name);
                    
                    switch (command.Length)
                    {
                        case 3:
                            builder.AppendFormat(listFormat,
                                new object[] { "Id", "State", "PlugTypeInfo", RuntimeCommands.lf });

                            foreach (var pti in plugType)
                                builder.AppendFormat(
                                    listFormat,
                                    new object[]
                                        {
                                            pti.Id,
                                            GetStateAsText(StateHelper.GetState(pti)),
                                            pti.Name,
                                            RuntimeCommands.lf
                                        });

                            builder.AppendFormat("  {0} item(s).{1}", plugType.Length, RuntimeCommands.lf);
                            break;
                        
                        case 4:
                            builder.AppendFormat(listFormat,
                                new object[] { "Id", "State", "ExtensionTypeInfo", RuntimeCommands.lf });

                            foreach (var extensionTypeInfo in plugType.Select(pti => pti.ExtensionTypeInfo))
                            {
                                builder.AppendFormat(
                                    listFormat,
                                    new object[]
                                        {
                                            extensionTypeInfo.Id,
                                            GetStateAsText(
                                                StateHelper.GetState(extensionTypeInfo)),
                                            extensionTypeInfo.Name,
                                            RuntimeCommands.lf
                                        });
                            }

                            builder.AppendFormat("  {0} item(s).{1}", plugType.Length, RuntimeCommands.lf);
                            break;
                        
                        default:
                            foreach (var pti in plugType)
                                pti.ExtensionTypeInfo.RegisterPlugs();

                            break;
                    }
                }
            }

            return builder.ToString();
        }

        private static string ListSlotInfos(string[] command)
        {
            var builder = new StringBuilder();

            if (command.Length == 2)
            {
                builder.AppendFormat(listFormat,
                    new object[] { "Id", "State", "SlotInfo", RuntimeCommands.lf });

                var i = 0;

                using (var enumerator = Runtime.Repository.SlotInfos.GetEnumerator())
                {
                    while (enumerator.MoveNext())
                    {
                        var current = enumerator.Current;

                        builder.AppendFormat(
                            listFormat,
                            new object[]
                                {
                                    current.Id,
                                    GetSlotStateAsText(StateHelper.GetSlotState(current)),
                                    current.Name,
                                    RuntimeCommands.lf
                                });
                        
                        i++;
                    }
                }

                builder.AppendFormat("  {0} item(s).{1}", i, RuntimeCommands.lf);
            }
            else
            {
                var index = command[2].IndexOf('"');
                var lastIndex = command[2].LastIndexOf('"');
                var si = new SlotInfo[0];

                if (index == -1)
                {
                    int item;

                    if (int.TryParse(command[2], out item))
                    {
                        var slot = Runtime.Repository.SlotInfos.Find(item);

                        if (slot != null)
                            si = new[] {slot};
                    }
                }
                else if (lastIndex != -1 && lastIndex > index)
                {
                    var name = command[2].Substring(index + 1, lastIndex - index - 1);
                    si = Runtime.Repository.SlotInfos.Find(name);
                }

                if (command.Length == 3)
                {
                    builder.AppendFormat(listFormat,
                        new object[] { "Id", "State", "SlotInfo", RuntimeCommands.lf });

                    foreach (var slot in si)
                        builder.AppendFormat(
                            listFormat,
                            new object[]
                                {
                                    slot.Id,
                                    GetSlotStateAsText(StateHelper.GetSlotState(slot)),
                                    slot.Name,
                                    RuntimeCommands.lf
                                });

                    builder.AppendFormat("  {0} item(s).{1}", si.Length, RuntimeCommands.lf);
                }
                else
                {
                    var cmd = command[3];
                    index = cmd.IndexOf('=');

                    if (index > 0 && cmd.Length > index++)
                    {
                        switch (cmd[index])
                        {
                            case 'F':
                            case 'f':
                                foreach (var slot in si)
                                    slot.AutoRegister = false;
                                break;
                            
                            case 'T':
                            case 't':
                                foreach (var slot in si)
                                    slot.AutoRegister = true;
                                break;
                        }
                    }
                    else
                    {
                        foreach (var slot in si)
                            slot.Register();
                    }
                }
            }

            return builder.ToString();
        }

        public CommandHandler Handler
        {
            get { return List; }
        }

        public string Help
        {
            get { return "Help for command \"list\""; }
        }

        public string Name
        {
            get { return "list"; }
        }
    }
}

